
import { _decorator, Component, Node, Vec2, v2, v3, random, Color, find } from 'cc';
import { Config } from '../Config';
import { Cell } from '../Constructs/Cell';
import { SplitArea } from '../Constructs/SplitArea';
import { TmpRoom } from '../Constructs/TmpRoom';
import { Direction } from '../Enums/Direction';
import { TmpRoomType } from '../Enums/TmpRoomType';
import { Renderer } from './Renderer';
const { ccclass, property } = _decorator;

/**
 * Predefined variables
 * Name = Calculator
 * DateTime = Thu Sep 02 2021 18:09:57 GMT+0800 (中国标准时间)
 * Author = Squall
 * FileBasename = Calculator.ts
 * FileBasenameNoExtension = Calculator
 * URL = db://assets/Scripts/Calculator.ts
 * ManualUrl = https://docs.cocos.com/creator/3.3/manual/zh/
 *
 */

@ccclass('Calculator')
export class Calculator extends Component {

    @property
    serializableDummy = 0;

    _roomId:number;

    start() {
        this._roomId = 1;
    }

    // update (deltaTime: number) {
    //     // [4]
    // }

    /**
     * 得到初始地图数组，左上角的row和column为0，0
     * @param totalWidth 
     * @param totalHeight 
     * @param cellWidth 
     * @param cellHeight 
     * @returns 
     */
    public getOriginMap(totalWidth, totalHeight,rowInterval, columnInterval): Array<Array<Cell>> {
        let cellWidth = Config.CellWidth;
        let cellHeight = Config.CellHeight;
        let mapArr = new Array<Array<Cell>>();
        let row = Math.floor(totalHeight / (cellHeight+rowInterval));
        let column = Math.floor(totalWidth / (cellWidth+columnInterval));
        let centerPos = Vec2.ZERO;
        let leftTopX = centerPos.x - (cellWidth * column + (column - 1) * columnInterval) / 2 + cellWidth / 2;
        let leftTopY = centerPos.y + (cellHeight * row + (row - 1) * rowInterval) / 2 - cellHeight / 2;
        let tmpId = 1;
        for (let i = 0; i < row; i++) {
            for (let j = 0; j < column; j++) {
                let cell: Cell = new Cell();
                cell.id = tmpId++;
                cell.column = j;
                cell.row = i;
                cell.pos = v3(leftTopX + j * cellWidth + columnInterval * j, leftTopY - i * cellHeight - i * rowInterval,0);

                if (!mapArr[i]) mapArr[i] = [];
                mapArr[i].push(cell);
            }
        }
        return mapArr;
    }

    public getRoomAnchors(roomCount:number,areaCells:Array<Cell>):Array<Cell>{
        let tmpCells = [].concat(areaCells)
        let anchors:Array<Cell> = [];
        for(let i=0;i<roomCount;i++){
            let cellIdx = Math.floor(tmpCells.length * Math.random());
            let anchorCell = tmpCells[cellIdx];
            if(!anchorCell)
                console.error("anchorCell为空");
            anchors.push(anchorCell);
            tmpCells.splice(cellIdx,1);
        }
        return anchors;
    }

    /**
     * 得到初始房间的生成范围的Cell数组，用于显示和随机取点
     * @param oriCells 地图数组
     * @returns 
     */
    public getRoomArea(oriCells:Array<Array<Cell>>):Array<Cell>{
        let totalRow = oriCells.length;
        let totalCol = oriCells[0].length;
        if(Config.originRoomArea > totalRow || Config.originRoomArea > totalCol){
            console.error("Config.originRoomArea的设置超过了地图最大长度，请重新设置");
            return;
        }

        let cRow = Math.floor(totalRow/2);
        let cCol = Math.floor(totalCol/2);
        let sRow = cRow - Config.originRoomArea;
        let sCol = cCol - Config.originRoomArea;

        let roomArea:Array<Cell> = [];
        for(let r = sRow;r<sRow+2*Config.originRoomArea;r++){
            for(let c = sCol;c<sCol+2*Config.originRoomArea;c++){
                roomArea.push(this.getCell(oriCells,r,c));
            }
        }
        
        return roomArea;
    }

    private getNewTempRoom(width,height,areaCells:Array<Cell>){
        let room:TmpRoom = new TmpRoom();
        room.width = width;
        room.height = height;
        let anchorCell = areaCells[Math.floor(areaCells.length * Math.random())];
        if(!anchorCell)
            console.error("anchorCell为空");
        return room;
    }

    /**
     * 根据锚点得到一个随机的Room
     * @param anchor 
     * @param mapCells 
     */
    public getRoomByAnchor(anchor:Cell,mapCells:Array<Array<Cell>>):TmpRoom{
        let roomWidth = Config.MinRoomWidth + Math.round(Math.random()*(Config.MaxRoomWidth-Config.MinRoomWidth));
        let roomHeight = Config.MinRoomWidth + Math.round(Math.random()*(Config.MaxRoomHeight-Config.MinRoomHeight));
        let startRow:number = anchor.row - Math.floor(Math.random()*roomHeight);
        let startCol:number = anchor.column - Math.floor(Math.random()*roomWidth);

        let newRoom:TmpRoom = new TmpRoom();
        newRoom.id = this._roomId++;
        newRoom.oriAnchor = anchor;
        newRoom.width = roomWidth;
        newRoom.height = roomHeight;
        newRoom.anchorRow = startRow;
        newRoom.anchorCol = startCol;
        for(let row=startRow;row<startRow+roomHeight;row++){
            for(let column=startCol;column<startCol+roomWidth;column++){
                newRoom.cells.push(this.getCell(mapCells,row,column));
            }
        }
        newRoom.roomColor = this._getRandomColor();
        newRoom.TmpRoomType = this._getRoomType(newRoom);
        this._updateStressCenter(newRoom);
        return newRoom;
    }

    public generateRoomWithLTAnchor(mapCells:Array<Array<Cell>>,anchor:Cell,roomWidth,roomHeight){
        let startRow:number = anchor.row;
        let startCol:number = anchor.column;

        let newRoom:TmpRoom = new TmpRoom();
        newRoom.id = this._roomId++;
        newRoom.oriAnchor = anchor;
        newRoom.width = roomWidth;
        newRoom.height = roomHeight;
        newRoom.anchorRow = startRow;
        newRoom.anchorCol = startCol;
        for(let row=startRow;row<startRow+roomHeight;row++){
            for(let column=startCol;column<startCol+roomWidth;column++){
                newRoom.cells.push(this.getCell(mapCells,row,column));
            }
        }
        newRoom.roomColor = this._getRandomColor();
        newRoom.TmpRoomType = this._getRoomType(newRoom);
        this._updateStressCenter(newRoom);
        return newRoom;
    }

    /**
     * 房间扩散一次，若无法扩散则返回false
     * @param cells 
     * @param rooms 
     */
    public spreadOnce(cells:Array<Array<Cell>>,rooms:Array<TmpRoom>):boolean{
        //更新当前受力
        let anyRoomOppressed:boolean = false;
        for(let r of rooms){
            r.clearStress();
            r.clearOverlapRooms();
        }
        for(let r of rooms){
            this._updateStressCenter(r);
            let opperessed = this._updateRoomStress(r,rooms);
            if(opperessed && !anyRoomOppressed){
                anyRoomOppressed = true;
            }
        }
        
        //根据受力改变坐标！
        if(!anyRoomOppressed) return false;
        for(let room of rooms){
            this._updateRoomByStress(room,cells);
        }
        return true;
    }

    /**
     * 随机选出指定数量的房间
     * @param rooms 
     */
    public getMainRooms(rooms:Array<TmpRoom>):Array<TmpRoom>{
        if(Config.MainRoomCount > rooms.length){
            console.warn("备选房间数量过少，无法选出",Config.MainRoomCount,"个主房间");
        }
        let tmpRooms = [].concat(rooms);
        let mainRooms = [];

        let _pickRdm = function(){
            let rdmIdx = Math.floor(Math.random()*tmpRooms.length);
            if(mainRooms.indexOf(tmpRooms[rdmIdx]) < 0){
                mainRooms.push(tmpRooms[rdmIdx]);
                tmpRooms.splice(rdmIdx,1);
                return true;
            }
            return false;
        }

        let pickBigRoom = function(){
            for(let i=0;i<tmpRooms.length;i++){
                let r = tmpRooms[i];
                if(r.width >= Config.MainRoomLength && r.height >= Config.MainRoomLength){
                    mainRooms.push(r);
                    tmpRooms.splice(i--,1);
                }
            }
        }

        let pickRandomRoom = function(roomCount){
            for(let i=0;i<roomCount;i++){
                if(tmpRooms.length === 0){
                    console.warn("房间数量过少，无法选出指定数量的房间，将返回全部房间作为结果");
                    break;
                }
                if(!_pickRdm()){
                    i--;
                }
            }
        }

        pickBigRoom();
        if(Config.MainRoomCount - mainRooms.length > 0){
            console.log("符合长宽规定的房间过少，将额外选取",Config.MainRoomCount - mainRooms.length,"个随机房间作为主房间")
            pickRandomRoom(Config.MainRoomCount - mainRooms.length);
        }
        return mainRooms;
    }

    //两个房间之间的走廊
    //参考https://www.jianshu.com/p/ca8142183034
    //把两个房间所有可能的联通方式都求出来，最后统一处理，去掉不合理的道路，每两个房间之间只留一条道路
    private _getRoadBetween(room1:TmpRoom,room2:TmpRoom,lines:Array<Array<number>>,points:Array<Array<number>>){
        //直连
            //横向直连，判断两个room纵向的边在横向轴的重叠部分，是否够铺一条路（3tile宽的路）
            
            //纵向直连，判断两个room横向的边在纵向轴上的重叠部分，是否能铺一条路

        //L连
            //Room2在Room1左上

            //Room2在Room1右上
            
            //Room2在Room1右下

            //Room2在Room1左下

        //返回路线对象
        
    }

    public getVecOfRooms(rooms:Array<TmpRoom>){
        let vecs : Array<Array<number>> = [];
        for(let r of rooms){
            vecs.push([r.stressCenterColumn,r.stressCenterRow])
        }
        return vecs;
    }

    public getLinesFromTriagles(triagles:Array<number>):Array<Array<number>>{
        let lines = [];
        let exists = function(ps,pe){
            for(let j=0;j<lines.length;j++){
                if(lines[j][0] === ps && lines[j][1] === pe) return true;
            }
            return false;
        }

        for(let i=0;i<triagles.length;i+=3){
            let p1 = triagles[i];
            let p2 = triagles[i+1];
            let p3 = triagles[i+2];
            if(!exists(p1,p2)) lines.push([p1,p2]);
            if(!exists(p2,p3)) lines.push([p2,p3]);
            if(!exists(p3,p1)) lines.push([p3,p1]);
        }
        return lines;
    }

    public getMinimumSpanningTree(){
        
    }

    /**
     * 根据房间当前受力，移动房间
     */
    private _updateRoomByStress(room:TmpRoom,mapCells:Array<Array<Cell>>){
        //hf是向右移动的距离，vf是向下移动的距离
        let hF = room.stressRight - room.stressLeft;
        let vF = room.stressDown - room.stressUp;

        if(room.overlapRooms.length > 1){//复杂情况，逃离
            let shortVF = 0;
            let shortHF = 0;
            let fleeVec:Vec2 = this.__getFleeVec(room);
            shortHF += fleeVec.x;
            shortVF += fleeVec.y;
            this._moveRoom(room,shortVF,shortHF,mapCells);
        }else if(this.__needIgnoreSecondForce(vF,hF,2)){//当横纵力中的一个远大于另一个时，用小的那个去移动
            hF = hF > 0 ? 1 : -1;
            this._moveRoom(room,0,hF,mapCells);
        }else if(this.__needIgnoreSecondForce(hF,vF,2)){//当横纵力中的一个远大于另一个时，用小的那个去移动
            vF = vF > 0 ? 1 : -1;
            this._moveRoom(room,vF,0,mapCells);
        }else{//当横纵力平衡
            let shortVF = 0;
            let shortHF = 0;
            if(vF!==0) shortVF = vF > 0 ? 1 : -1;
            if(hF!==0) shortHF = hF > 0 ? 1 : -1;
            if(vF === 0 && hF === 0){//确实没有任何受力，靠惯性动一下
                if(room.inertia !== null){
                    shortHF += room.inertia.x;
                    shortVF += room.inertia.y;
                    room.inertia = null;
                    // console.error("惯性");
                }else if(room.stressRight !== 0 || room.stressLeft !== 0 
                    || room.stressDown !==0 || room.stressUp !== 0){//有受力但各力平衡，逃离
                    // console.error(room.id+"开始逃离",room.roomColor);
                    let fleeVec:Vec2 = this.__getFleeVec(room);
                    shortHF += fleeVec.x;
                    shortVF += fleeVec.y;
                    // console.error("逃离");
                }
            }
            this._moveRoom(room,shortVF,shortHF,mapCells);
        }
    }

    /**
     * 得到向房间少的地方逃跑的向量
     */
    private __getFleeVec(room:TmpRoom):Vec2{
        let overlapRooms:Array<TmpRoom> = room.overlapRooms;
        let finalMoveVec:Vec2 = v2(0,0);
        // console.log("有",overlapRooms.length,"个房间压着");
        for(let r of overlapRooms){
            let slefVec:Vec2 = v2(room.stressCenterColumn,room.stressCenterRow);
            let tmpCenter = v2(r.stressCenterColumn,r.stressCenterRow);
            // console.log("两个初始向量：",slefVec,tmpCenter);
            slefVec.subtract(tmpCenter);
            // console.log("向量：",slefVec);
            finalMoveVec.add(slefVec);
        }
        // console.log("向量结果：",finalMoveVec);
        finalMoveVec.normalize();
        if(finalMoveVec.x !==0) finalMoveVec.x =  Math.round(finalMoveVec.x);
        if(finalMoveVec.y !==0) finalMoveVec.y =  Math.round(finalMoveVec.y);
        if(finalMoveVec.x !== 0 || finalMoveVec.y !== 0){
            return finalMoveVec;
        }else{
            return v2(this.__getRdmStep(),this.__getRdmStep());
        }
    }

    private __needIgnoreSecondForce(f1,f2,threshold){
        return Math.abs(Math.abs(f1) - Math.abs(f2))>threshold && Math.abs(f1) > Math.abs(f2) && f2 !== 0
    }

    private _getFinalVForce(room:TmpRoom){
        let vF = room.stressRight - room.stressLeft;
        if(vF!==0) vF = vF > 0 ? 1 : -1;
        return vF;
    }
    private _getFinalHForce(room:TmpRoom){
        let hF = room.stressDown - room.stressUp;
        if(hF!==0) hF = hF > 0 ? 1 : -1;
        return hF;
    }

    //hf是向右移动的距离，vf是向下移动的距离
    private _moveRoom(room:TmpRoom,vF:number,hF:number,mapCells:Array<Array<Cell>>){
        room.anchorRow += vF;
        room.anchorCol += hF;
        if(!room.inertia) room.inertia = new Vec2(0,0);
        room.inertia.x = hF;
        room.inertia.y = vF;
        this._updateRoomsCells(mapCells,room);
        // console.log("room:",room.id," 受力移动hF:",hF," vF:",vF);
    }

    private _updateRoomsCells(mapCells:Array<Array<Cell>>,room:TmpRoom){
        room.cells = [];
        for(let row=room.anchorRow;row<room.anchorRow+room.height;row++){
            for(let column=room.anchorCol;column<room.anchorCol+room.width;column++){
                room.cells.push(this.getCell(mapCells,row,column));
            }
        }
    }

    /**
     * 重叠Cell会对房间施加力
     * @param room 
     * @param allRoom 
     * @returns 
     */
    private _updateRoomStress(room:TmpRoom,allRoom:Array<TmpRoom>):boolean{
        let anyRoomOppressed : boolean = false;
        let roomCells:Array<Cell> = room.cells;
        //施加过横向力的列
        let forcedColumns:Array<number> = [];
        //施加过纵向力的行
        let forcedRows:Array<number> = [];
        for(let cell of roomCells){
            let addForcedColumn = function(column:number){
                if(!forcedColumns.includes(column)){
                    // console.log("forcedColumn +1",forcedColumns.length," ",column)
                    forcedColumns.push(cell.column);
                    return true;
                }
                return false;
            }
            let addForcedRow = function(row:number){
                if(!forcedRows.includes(row)){
                    // console.log("forcedRow+1",forcedRows.length," ",row)
                    forcedRows.push(cell.row);
                    return true;
                }
                return false;
            }

            for(let testRoom of allRoom){
                if(testRoom.id === room.id) continue;

                let cellOverlap:boolean = false;
                let overlapRoomCount:number = 0;
                if(this._isCellIncludeInRoom(cell,testRoom)){
                    cellOverlap = true;
                    overlapRoomCount++;
                    room.addOverlapRoom(testRoom);
                    if(!anyRoomOppressed) anyRoomOppressed = true;
                }

                if(cellOverlap){
                    if(room.TmpRoomType === TmpRoomType.CAO){
                        if(cell.row > room.stressCenterRow){
                            if(addForcedRow(cell.row)) room.stressUp += overlapRoomCount;
                        }else{
                            if(addForcedRow(cell.row)) room.stressDown += overlapRoomCount;
                        }
                        if(cell.column > room.stressCenterColumn){
                            if(addForcedColumn(cell.column)) room.stressLeft += overlapRoomCount;
                        }else if(cell.column < room.stressCenterColumn){
                            if(addForcedColumn(cell.column)) room.stressRight += overlapRoomCount;
                        }
                    }else if(room.TmpRoomType === TmpRoomType.YONG){
                        if(cell.row > room.stressCenterRow){
                            if(addForcedRow(cell.row)) room.stressUp += overlapRoomCount;
                        }else if(cell.row < room.stressCenterRow){
                            if(addForcedRow(cell.row)) room.stressDown += overlapRoomCount;
                        }
                        if(cell.column > room.stressCenterColumn){
                            if(addForcedColumn(cell.column)) room.stressLeft += overlapRoomCount;
                        }else{
                            if(addForcedColumn(cell.column)) room.stressRight += overlapRoomCount;
                        }
                    }else if(room.TmpRoomType === TmpRoomType.JING){
                        if(cell.row > room.stressCenterRow){
                            if(addForcedRow(cell.row)) room.stressUp += overlapRoomCount;
                        }else if(cell.row < room.stressCenterRow){
                            if(addForcedRow(cell.row)) room.stressDown += overlapRoomCount;
                        }
                        if(cell.column > room.stressCenterColumn){
                            if(addForcedColumn(cell.column)) room.stressLeft += overlapRoomCount;
                        }else if(cell.column < room.stressCenterColumn){
                            if(addForcedColumn(cell.column)) room.stressRight += overlapRoomCount;
                        }
                    }else if(room.TmpRoomType === TmpRoomType.TIAN){
                        if(cell.row > room.stressCenterRow){
                            if(addForcedRow(cell.row)) room.stressUp += overlapRoomCount;
                        }else{
                            if(addForcedRow(cell.row)) room.stressDown += overlapRoomCount;
                        }
                        if(cell.column < room.stressCenterColumn){
                            if(addForcedColumn(cell.column)) room.stressRight += overlapRoomCount;
                        }else{
                            if(addForcedColumn(cell.column)) room.stressLeft += overlapRoomCount;
                        }
                    }else if(room.TmpRoomType === TmpRoomType.NONE){
                        console.error("未知的房间类型，无法计算房间扩散时的受力情况")
                    }
                }
            }
        }
        return anyRoomOppressed;
    }

    private _isCellIncludeInRoom(cell:Cell,room:TmpRoom){
        if(!room.cells){
            console.error("cell is null ",room.cells)
        }
        let testCells:Array<Cell> = room.cells;
        for(let c of testCells){
            if(c.row === cell.row && c.column === cell.column) return true;
        }
        return false;
    }

    public getCell(cells:Array<Array<Cell>>,row:number,column:number){
        if(!cells[row]) return null;
        if(!cells[row][column]) return null;
        return cells[row][column];
    }

    private _getRoomType(room:TmpRoom):number{
        if(!room.cells || !room.cells.length){
            console.warn("房间未初始化");
            return TmpRoomType.NONE;
        }

        let rH = room.height;
        let rW = room.width;
        if(this.isEvenNumber(rW)){
            if(this.isEvenNumber(rH)){
                return TmpRoomType.TIAN;
            }else{
                return TmpRoomType.YONG;
            }
        }else{
            if(this.isEvenNumber(rH)){
                return TmpRoomType.CAO;
            }else{
                return TmpRoomType.JING;
            }
        }
    }

    private _updateStressCenter(room:TmpRoom){
        let rH = room.height;
        let rW = room.width;
        room.stressCenterRow = room.anchorRow + rH/2 - 0.5;
        room.stressCenterColumn = room.anchorCol + rW/2 - 0.5;
    }

    private getRandomCell(cells:Array<Array<Cell>>){
        let row = Math.floor(Math.random()*cells.length);
        let column = Math.floor(Math.random()*cells[0].length);

        return cells[row][column];
    }

    private isEvenNumber(target:number){
        return target % 2 === 0;
    }

    //返回0或1或-1
    private __getRdmStep(){
        let rdm = Math.random();
        return rdm>0.66?1:rdm>0.33?-1:0;
    }

    private getRdmSymbol():number{
        return Math.random()>0.5?1:-1;
    }

    private _getRandomColor(){
        let r = Math.round(Math.random()*255);
                let g = Math.round(Math.random()*255);
                let b = Math.round(Math.random()*255);
                return new Color(r,g,b,255);
    }
}

/**
 * [1] Class member could be defined like this.
 * [2] Use `property` decorator if your want the member to be serializable.
 * [3] Your initialization goes here.
 * [4] Your update function goes here.
 *
 * Learn more about scripting: https://docs.cocos.com/creator/3.3/manual/zh/scripting/
 * Learn more about CCClass: https://docs.cocos.com/creator/3.3/manual/zh/scripting/ccclass.html
 * Learn more about life-cycle callbacks: https://docs.cocos.com/creator/3.3/manual/zh/scripting/life-cycle-callbacks.html
 */
